package org.geektimes.projects.user.repository;

import org.geekbang.projects.injection.ApplicationContext;
import org.geekbang.projects.injection.Component;
import org.geekbang.projects.injection.processor.ApplicationContextAware;

import javax.annotation.PostConstruct;
import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.metamodel.Metamodel;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
*
 *   EntityManager工程
* @author : lsq
*/
@Component
public class EntityManagerBeanFactory implements EntityManager , ApplicationContextAware {

	private javax.persistence.EntityManagerFactory entityManagerFactory;


	private Properties properties;


	private String emf;


	private ThreadLocal<EntityManager> managerThreadLocal = new ThreadLocal<>();



	private ApplicationContext applicationContext;



	public EntityManagerBeanFactory(Properties properties ,String emf ){
		this.properties = properties;
		this.emf = emf;
	}

	/**
	 * 初始化工程
	 */
	@PostConstruct
	public void init() {
		Enumeration<?> enumeration = properties.propertyNames();
		while (enumeration.hasMoreElements()){
			Object key = enumeration.nextElement();
			Object value = properties.get(key);
			if (value.toString().startsWith("@")){
				properties.put(key , applicationContext.lookup(value.toString().substring(1)).getBean());
			}
		}
		this.entityManagerFactory = Persistence.createEntityManagerFactory(emf, properties);
	}


	/**
	 * 获取EntityManager
	 * @return
	 */
	protected EntityManager getEntityManager(){
		EntityManager entityManager = managerThreadLocal.get();
		if (entityManager == null) {
			entityManager = entityManagerFactory.createEntityManager();
			managerThreadLocal.set(entityManager);
		}
		return entityManager;
	}



	@Override
	public void persist(Object entity) {
		getEntityManager().persist(entity);
	}

	@Override
	public <T> T merge(T entity) {
		return getEntityManager().merge(entity);
	}

	@Override
	public void remove(Object entity) {
		getEntityManager().remove(entity);
	}

	@Override
	public <T> T find(Class<T> entityClass, Object primaryKey) {
		return getEntityManager().find(entityClass, primaryKey);
	}

	@Override
	public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties) {
		return getEntityManager().find(entityClass, primaryKey, properties);
	}

	@Override
	public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode) {
		return getEntityManager().find(entityClass, primaryKey, lockMode);
	}

	@Override
	public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties) {
		return getEntityManager().find(entityClass, primaryKey, lockMode, properties);
	}

	@Override
	public <T> T getReference(Class<T> entityClass, Object primaryKey) {
		return getEntityManager().find(entityClass, primaryKey);
	}

	@Override
	public void flush() {
		getEntityManager().flush();
	}

	@Override
	public void setFlushMode(FlushModeType flushMode) {
		getEntityManager().setFlushMode(flushMode);
	}

	@Override
	public FlushModeType getFlushMode() {
		return getEntityManager().getFlushMode();
	}

	@Override
	public void lock(Object entity, LockModeType lockMode) {
		getEntityManager().lock(entity, lockMode);
	}

	@Override
	public void lock(Object entity, LockModeType lockMode, Map<String, Object> properties) {
		getEntityManager().lock(entity, lockMode, properties);
	}

	@Override
	public void refresh(Object entity) {
		getEntityManager().refresh(entity);
	}

	@Override
	public void refresh(Object entity, Map<String, Object> properties) {
		getEntityManager().refresh(entity, properties);
	}

	@Override
	public void refresh(Object entity, LockModeType lockMode) {
		getEntityManager().refresh(entity, lockMode);
	}

	@Override
	public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties) {
		getEntityManager().refresh(entity, lockMode, properties);
	}

	@Override
	public void clear() {
		getEntityManager().clear();
	}

	@Override
	public void detach(Object entity) {
		getEntityManager().detach(entity);
	}

	@Override
	public boolean contains(Object entity) {
		return getEntityManager().contains(entity);
	}

	@Override
	public LockModeType getLockMode(Object entity) {
		return getEntityManager().getLockMode(entity);
	}

	@Override
	public void setProperty(String propertyName, Object value) {
		getEntityManager().setProperty(propertyName, value);
	}

	@Override
	public Map<String, Object> getProperties() {
		return getEntityManager().getProperties();
	}

	@Override
	public Query createQuery(String qlString) {
		return getEntityManager().createQuery(qlString);
	}

	@Override
	public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) {
		return getEntityManager().createQuery(criteriaQuery);
	}

	@Override
	public Query createQuery(CriteriaUpdate updateQuery) {
		return getEntityManager().createQuery(updateQuery);
	}

	@Override
	public Query createQuery(CriteriaDelete deleteQuery) {
		return getEntityManager().createQuery(deleteQuery);
	}

	@Override
	public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass) {
		return getEntityManager().createQuery(qlString, resultClass);
	}

	@Override
	public Query createNamedQuery(String name) {
		return getEntityManager().createNamedQuery(name);
	}

	@Override
	public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass) {
		return getEntityManager().createNamedQuery(name, resultClass);
	}

	@Override
	public Query createNativeQuery(String sqlString) {
		return getEntityManager().createNativeQuery(sqlString);
	}

	@Override
	public Query createNativeQuery(String sqlString, Class resultClass) {
		return getEntityManager().createNativeQuery(sqlString, resultClass);
	}

	@Override
	public Query createNativeQuery(String sqlString, String resultSetMapping) {
		return getEntityManager().createNativeQuery(sqlString, resultSetMapping);
	}

	@Override
	public StoredProcedureQuery createNamedStoredProcedureQuery(String name) {
		return getEntityManager().createNamedStoredProcedureQuery(name);
	}

	@Override
	public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
		return getEntityManager().createStoredProcedureQuery(procedureName);
	}

	@Override
	public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) {
		return getEntityManager().createStoredProcedureQuery(procedureName , resultClasses);
	}

	@Override
	public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) {
		return getEntityManager().createStoredProcedureQuery(procedureName , resultSetMappings);
	}

	@Override
	public void joinTransaction() {
		getEntityManager().joinTransaction();
	}

	@Override
	public boolean isJoinedToTransaction() {
		return getEntityManager().isJoinedToTransaction();
	}

	@Override
	public <T> T unwrap(Class<T> cls) {
		return getEntityManager().unwrap(cls);
	}

	@Override
	public Object getDelegate() {
		return getEntityManager().getDelegate();
	}

	@Override
	public void close() {
		getEntityManager().close();
	}

	@Override
	public boolean isOpen() {
		return getEntityManager().isOpen();
	}

	@Override
	public EntityTransaction getTransaction() {
		return getEntityManager().getTransaction();
	}

	@Override
	public EntityManagerFactory getEntityManagerFactory() {
		return getEntityManager().getEntityManagerFactory();
	}

	@Override
	public CriteriaBuilder getCriteriaBuilder() {
		return getEntityManager().getCriteriaBuilder();
	}

	@Override
	public Metamodel getMetamodel() {
		return getEntityManager().getMetamodel();
	}

	@Override
	public <T> EntityGraph<T> createEntityGraph(Class<T> rootType) {
		return getEntityManager().createEntityGraph(rootType);
	}

	@Override
	public EntityGraph<?> createEntityGraph(String graphName) {
		return getEntityManager().createEntityGraph(graphName);
	}

	@Override
	public EntityGraph<?> getEntityGraph(String graphName) {
		return getEntityManager().getEntityGraph(graphName);
	}

	@Override
	public <T> List<EntityGraph<? super T>> getEntityGraphs(Class<T> entityClass) {
		return getEntityManager().getEntityGraphs(entityClass);
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}
}
